--Function that gets dbms name
create or replace procedure meta_get_dbms_name(
   a_product_name out varchar2)
as
begin
    a_product_name := 'ORACLE';
end;

--go

--Function that gets dbms version
create or replace procedure meta_get_dbms_version(
    a_dbms_version out varchar2)
as
begin
    select version into a_dbms_version
    from product_component_version
    where product like '%Oracle%';
end;

--go

--Function that return database name
create or replace procedure meta_get_current_db_name(
    a_db_name out varchar2)
as
begin
    select sys_context('USERENV', 'DB_NAME') into a_db_name from dual;
end;

--go

--Function of getting table name
create or replace function p_get_table_name(
    --Table id
    a_table_id raw)
    return varchar2
as
    --Table name
    target_table_name varchar2(30) := null;
begin
    if a_table_id is null then
        return null;
    end if;

    select table_name into target_table_name 
    from meta_tables where table_id = a_table_id;
    
    return target_table_name;
end;

--go

--Function of dropping a table
create or replace procedure p_drop_table_if_exists(
    --Target table name
    a_table_name varchar2)
as
    table_count integer;
begin
    select count(*)  into table_count from user_objects
    where object_name = upper(a_table_name)
    and object_type = 'TABLE';
    
    if  table_count > 0 then
        execute immediate 'drop table ' || upper(a_table_name) 
        || ' cascade constraints';
    end if;
end;

--go

--Function of dropping a view
create or replace procedure p_drop_view_if_exists(
    --Target view name
    a_view_name varchar2)
as
    view_count integer;
begin
    select count(*)  into view_count from user_objects
    where object_name = upper(a_view_name)
    and object_type = 'VIEW';
    
    if  view_count > 0 then
        execute immediate 'drop view ' || upper(a_view_name) 
        || ' cascade constraints';
    end if;
end;

--go

--Function of soft deleting a table row
create or replace procedure meta_soft_delete_table_row(
    --Table id
    a_table_id raw,
    --Row id
    a_row_id raw)
as
    --Current row id
    a_curr_row_id raw(16);
    --Current table name
    a_curr_table_name varchar2(26);
    --Current table id
    a_curr_table_id raw(16);
    --Ref table id
    a_ref_table_id raw(16);
    --Ref table name
    a_ref_table_name varchar2(26);
    --Ref column name
    a_ref_column_name varchar2(26);
    --Row count
    a_row_count number;
    --Current user
    a_curr_user varchar2(26);
    --Current date
    a_curr_date date;
begin
    --Getting the current user and date
    a_curr_user := lower(user);
    a_curr_date := sysdate;

    --Saying that we want to delete the initial row
    insert into meta_rows values(a_row_id, a_table_id, 0);
    
    loop    
        --Getting a row to be deleted
        select table_id, row_id
        into a_curr_table_id, a_curr_row_id
        from meta_rows where is_used=0 and rownum < 2;
        
        a_curr_table_name := p_get_table_name(a_curr_table_id);
         
         --Enumerating all references on this table
         for ref in (select column_id, cascade_delete from meta_ref_targets
             where ref_table_id = a_curr_table_id) loop
                 
             --Reading ref column properties
             select column_name, table_id
             into a_ref_column_name, a_ref_table_id
             from meta_ref_columns
             where column_id = ref.column_id;
                 
             a_ref_table_name := p_get_table_name(a_ref_table_id);
             
             if ref.cascade_delete = 0 then
                 --Checking reference value existence
                 execute immediate 'select count(*) from dual where exists(select 1 from tbl_' || a_ref_table_name
                 || ' where ' || a_ref_column_name || '= :1 and is_active=1)' 
                 into a_row_count
                 using a_curr_row_id;
                 
                 --If there are reference values and cascade delete is forbidden
                 if a_row_count > 0 then
                    raise_application_error(-20001,
                        'Deleting when there are references on the row (id = '
                        || a_row_id 
                        || ') with CascadeDelete = false (#error_code = 20001)');
                 end if;
             end if;
             
            --Deleting other reference values too (putting them into the list of the rows to be deleted)
             execute immediate 'insert into meta_rows(row_id, table_id)'
             || 'select id, :1 from tbl_' || a_ref_table_name
             || ' where ' || a_ref_column_name || ' = :2'
             || ' and id not in (select row_id from meta_rows)'
             using a_ref_table_id, a_curr_row_id;
         end loop;
         
        --Deleting the current row
        execute immediate 'update tbl_' || a_curr_table_name
        || ' set is_active = 0, deleted_on = :1, deleted_by = :2 where id=:3'
        using a_curr_date,  a_curr_user, a_curr_row_id;
            
        --Adding the current row to the deleted rows list
        update meta_rows
        set is_used = 1
        where row_id = a_curr_row_id;
        
        select count(*) into a_row_count
        from meta_rows where is_used = 0 and rownum < 2;
            
        exit when a_row_count = 0;    
     end loop;
end;

--go

--Function of restoring a table row
create or replace procedure meta_restore_table_row(
    --Target id
    a_table_id raw,
    --Row id
    a_row_id raw)
as
    --Current row id
    a_curr_row_id raw(16);
    --Current table name
    a_curr_table_name varchar2(26);
    --Current table id
    a_curr_table_id raw(16);
    --Ref table name
    a_ref_table_name varchar2(26);
    --Row count
    a_row_count number;
    --Reference value
    a_ref_value raw(16);
    --Reference value was found
    a_ref_value_was_found number(1) := 0;
begin
    --Saying that we want to restore the initial row
     insert into meta_rows values(a_row_id, a_table_id, 0);
    
    loop    
        --Getting a row to be restored
        select table_id, row_id
        into a_curr_table_id, a_curr_row_id
        from meta_rows where is_used=0 and rownum < 2;

        a_curr_table_name := p_get_table_name(a_curr_table_id);
            
         --Enumerating all references in this table
         for ref in (select column_id, column_name from meta_ref_columns
             where table_id = a_curr_table_id) loop
                 
             --Reading reference value
             execute immediate 'select ' || ref.column_name
             || ' from tbl_' || a_curr_table_name
             || ' where id = :1'
             into a_ref_value
             using a_curr_row_id;
                 
             if a_ref_value is not null then
                    
                a_ref_value_was_found := 0;
                    
                --Enumerating all referenced tables
                for ref_table in (select ref_table_id from meta_ref_targets
                    where column_id = ref.column_id) loop
                        
                    a_ref_table_name := p_get_table_name(ref_table.ref_table_id);
                 
                    --Checking reference value existence
                    execute immediate 'select count(*) from tbl_' 
                    || a_ref_table_name || ' where id = :1'
                    into a_row_count
                    using a_ref_value;
                 
                    --If there are reference values
                    if a_row_count > 0 then
                       --Restoring other reference values too (putting them into the list of rows to be restored)                           
                        execute immediate 'insert into meta_rows(row_id, table_id)'
                        || ' select :1, :2 from dual'
                        || ' where :3 not in (select row_id from meta_rows)'
                        using a_ref_value, ref_table.ref_table_id, a_ref_value;
                        
                        a_ref_value_was_found := 1;
                    end if;
                 end loop;
                     
                 --If reference value was not found
                 if a_ref_value_was_found = 0 then
                    raise_application_error(-20002,
                    'Restoring when there is no referenced row (id = '
                    || a_row_id || ', ref_value = '
                    || a_ref_value
                    || ') (#error_code = 20002)');
                 end if;                        
             end if;
         end loop;
             
         --Restoring the row
        execute immediate 'update tbl_' || a_curr_table_name || ' set is_active = 1,'
        || ' deleted_on = null, deleted_by = null where id = :1'
        using a_curr_row_id;
            
        --Adding the current row to the restored rows list
        update meta_rows
        set is_used = 1
        where row_id = a_curr_row_id;
        
        select count(*) into a_row_count
        from meta_rows where is_used = 0 and rownum < 2;
            
        exit when a_row_count = 0;    
    end loop;   
end;

--go

--Function of hard deleting a table row
create or replace procedure meta_delete_table_row(
    --Target id
    a_table_id raw,
    --Row id
    a_row_id raw)
as
    --Current row id
    a_curr_row_id raw(16);
    --Current table name
    a_curr_table_name varchar2(26);
    --Current table id
    a_curr_table_id raw(16);
    --Ref table id
    a_ref_table_id raw(16);
    --Ref table name
    a_ref_table_name varchar2(26);
    --Ref column name
    a_ref_column_name varchar2(26);
    --Row count
    a_row_count number;
begin
   
    --Saying that we want to delete the initial row
    insert into meta_rows values(a_row_id, a_table_id, 0);
    
    loop    
        --Getting a row to be deleted
        select table_id, row_id
        into a_curr_table_id, a_curr_row_id
        from meta_rows where is_used=0 and rownum < 2;
        
        a_curr_table_name := p_get_table_name(a_curr_table_id);
        
        --Enumerating all references on this table
        for ref in (select column_id, cascade_delete from meta_ref_targets
         where ref_table_id = a_curr_table_id) loop
                         
            --Reading ref column properties
            select column_name, table_id
            into a_ref_column_name, a_ref_table_id
            from meta_ref_columns
            where column_id = ref.column_id;
                         
            a_ref_table_name := p_get_table_name(a_ref_table_id);
                                                  
            --Deleting other reference values too (putting them into the list of the rows to be deleted)
            execute immediate 'insert into meta_rows(row_id, table_id)'
            || 'select id, :1 from tbl_' || a_ref_table_name
            || ' where ' || a_ref_column_name || ' = :2'
            || ' and id not in (select row_id from meta_rows)'
            using a_ref_table_id, a_curr_row_id;
        end loop;
                 
        --Deleting the current row
        execute immediate 'delete from tbl_' || a_curr_table_name
        || ' where id=:1'
        using a_curr_row_id;
                    
        --Adding the current row to the deleted rows list
        update meta_rows
        set is_used = 1
        where row_id = a_curr_row_id;
        
        select count(*) into a_row_count
        from meta_rows where is_used = 0 and rownum < 2;
            
        exit when a_row_count = 0;      
     end loop;
end;

--go

--Function of creating table triggers
create or replace procedure p_create_table_triggers(
    --Table id
    a_table_id raw)
as
    --Table name
    a_table_name varchar2(26);
begin
    a_table_name := p_get_table_name(a_table_id);    

    --Trigger before inserting a row
    execute immediate 'create trigger i'
    || substr(a_table_id, 1, 29) 
    || ' before insert on tbl_' || a_table_name
    || ' for each row'
    || ' begin'
    || ' if :new.id is null then'
    || ' :new.id := sys_guid;'
    || ' end if;'
    || ' if :new.owner is null then'
    || ' :new.owner := lower(user);'
    || ' else'
    || ' :new.owner := lower(:new.owner);'
    || ' end if;'
    || ' :new.created_on := sysdate;'
    || ' :new.updated_on := sysdate;'
    || ' :new.created_by := lower(user);'
    || ' :new.updated_by := lower(user);'
    || ' :new.row_version := sys_guid;'
    || ' end;';
end;

--go

--Function of creating table view
create or replace procedure p_create_table_view(
    --Table id
    a_table_id raw)
as
    --Table name
    table_name varchar2(26);
    --Unit level security
    a_unit_level_security numeric(1);
    --Recycle bin option
    a_recycle_bin_option numeric(10);
    --Comma delimited list of column names
    column_name_list varchar2(32767) := '';
    --Create view sql
    a_create_view_sql varchar2(32767);
    --Update trigger sql
    a_update_trigger_sql varchar2(32767);
    --Delete trigger sql
    a_delete_trigger_sql varchar2(32767);
begin
    table_name := p_get_table_name(a_table_id);
    
    --Reading table properties
    select unit_level_security into a_unit_level_security
    from meta_tables
    where table_id = a_table_id;
    
    --Reading options
    select int_value into a_recycle_bin_option
    from meta_options
    where option_name = 'RECYCLE_BIN';
    
    for col in (select column_name, column_type from meta_table_columns
         where table_id = a_table_id order by order_index) loop
         if col.column_name <> 'is_active' and
            col.column_name <> 'deleted_by' and
            col.column_name <> 'deleted_on' then
            
            if length(column_name_list) > 0 then
                column_name_list := column_name_list || ',';
            end if;
            
            column_name_list := column_name_list || col.column_name;
          end if;
    end loop;

    --Creating the view
    a_create_view_sql := 'create or replace view '
    || table_name
    || ' as select '
    || column_name_list || ' from tbl_'
    || table_name || ' t'
    || ' where 1 = 1';
    
    --If recycle bin is enabled
    if a_recycle_bin_option = 1 then
        a_create_view_sql := a_create_view_sql || ' and is_active = 1';    
    end if;
    
    --If unit level security is ON
    if a_unit_level_security = 1 then
        --The full access
        a_create_view_sql := a_create_view_sql || ' and (exists(select 1 from meta_table_acl_full_access where table_id=hextoraw('''
        || a_table_id || ''') and user_name=lower(user) and access_type=1)'
        --The unit level access
        || ' or t.owner in (select u.user_name from meta_table_acl_unit_access s inner join meta_user_units u on s.unit_id=u.unit_id'
        || ' where table_id=hextoraw(''' || a_table_id || ''') and s.user_name=lower(user) and access_type=1)'
        --The own rows access
        || ' or t.owner=lower(user) and exists(select 1 from meta_table_acl_own_rows where table_id=hextoraw('''
        || a_table_id || ''') and user_name=lower(user) and access_type=1))';
    end if;
    
    execute immediate a_create_view_sql;
    
    --Creating the delete trigger
    a_delete_trigger_sql := 'create or replace trigger d'
    || substr(a_table_id, 1, 29)
    || ' instead of delete on '
    || table_name
    || ' for each row'
    || ' declare'
    || ' result number;'
    || ' begin';
    
    --If unit level security is ON
    if a_unit_level_security = 1 then
        --Checking the delete permission
        a_delete_trigger_sql := a_delete_trigger_sql || ' select count(*) into result from dual'
        --The full access
        || ' where exists(select 1 from meta_table_acl_full_access where table_id=hextoraw('''
        || a_table_id || ''') and user_name=lower(user) and access_type=4)'
        --The unit-level access
        || ' or :old.owner in (select u.user_name from meta_table_acl_unit_access s inner join meta_user_units u on s.unit_id=u.unit_id'
        || ' where table_id=hextoraw(''' || a_table_id || ''') and s.user_name=lower(user) and access_type=4)'
        --The own rows access
        || ' or :old.owner=lower(user) and exists(select 1 from meta_table_acl_own_rows where table_id=hextoraw('''
        || a_table_id || ''') and user_name=lower(user) and access_type=4);'
        || ' if result = 0 then'
        || ' raise_application_error(-20004,'
        || '''Insufficient privileges to delete the row (id = '' || :old.id || '') (#error_code = 20004)'');'
        || ' end if;';
    end if;    
    
    --If recycle bin is enabled
    if a_recycle_bin_option = 1 then
        --Performing the soft delete
        a_delete_trigger_sql := a_delete_trigger_sql || ' meta_soft_delete_table_row(''' || a_table_id || ''', :old.id);';
    else
        --Performing the delete
        a_delete_trigger_sql := a_delete_trigger_sql || ' meta_delete_table_row(''' || a_table_id || ''', :old.id);';
    end if;
    
    a_delete_trigger_sql := a_delete_trigger_sql || ' end;';
    
    execute immediate a_delete_trigger_sql;
    
    --Creating update trigger
    a_update_trigger_sql := 'create trigger j'
    || substr(a_table_id, 1, 29)
    || ' instead of update on ' || table_name
    || ' for each row'
    || ' declare'
    || ' a_set_list varchar2(32767);'
    || ' result number;'
    || ' begin';
    
    --If unit level security is ON
    if a_unit_level_security = 1 then
        --Checking the update permission
         a_update_trigger_sql := a_update_trigger_sql || ' select count(*) into result from dual'
        --The full access
        || ' where exists(select 1 from meta_table_acl_full_access where table_id=hextoraw('''
        || a_table_id || ''') and user_name=lower(user) and access_type=3)'
        --The unit-level access
        || ' or :old.owner in (select u.user_name from meta_table_acl_unit_access s inner join meta_user_units u on s.unit_id=u.unit_id'
        || ' where table_id=hextoraw(''' || a_table_id || ''') and s.user_name=lower(user) and access_type=3)'
        --The own rows access
        || ' or :old.owner=lower(user) and exists(select 1 from meta_table_acl_own_rows where table_id=hextoraw('''
        || a_table_id || ''') and user_name=lower(user) and access_type=3);'
        || ' if result = 0 then'
        || ' raise_application_error(-20003,'
        || '''Insufficient privileges to update the row (id = '' || :old.id || '') (#error_code = 20003)'');'
        || ' end if;';
    end if;
    
    --Performing the update
    a_update_trigger_sql := a_update_trigger_sql || ' update tbl_' || table_name
    || ' set owner=lower(:new.owner), updated_on=sysdate, updated_by=lower(user), row_version=sys_guid';
        
     for col in (select column_name from meta_table_columns
         where table_id = a_table_id and customization_level > 0) loop
        a_update_trigger_sql := a_update_trigger_sql
        || ',' || col.column_name || '=:new.' || col.column_name;
     end loop;
    
    a_update_trigger_sql := a_update_trigger_sql
    || ' where id=:new.id;'
    || ' end;';
    
    execute immediate a_update_trigger_sql;   
    
    --Setting table view grants
    p_set_table_view_grants(a_table_id);
end;

--go

--Function of setting table view grants
create or replace procedure p_set_table_view_grants(
    --Table id
    a_table_id raw)
as
    --Table name
    a_table_name varchar2(26);
begin
    a_table_name := p_get_table_name(a_table_id);

    for acl in (select access_type, principal_name from meta_table_acl
        where table_id = a_table_id) loop
        
        if acl.access_type = 1 then
            execute immediate 'grant select on ' || a_table_name || ' to ' || acl.principal_name;
        elsif acl.access_type = 2 then
            execute immediate 'grant insert on ' || a_table_name || ' to ' || acl.principal_name;
        elsif acl.access_type = 3 then
            execute immediate 'grant update on ' || a_table_name || ' to ' || acl.principal_name;
        elsif acl.access_type = 4 then
            execute immediate 'grant delete on ' || a_table_name || ' to ' || acl.principal_name;
        end if;
        
    end loop;
end;

--go

--Function of checking that table doesn't exist
create or replace procedure p_check_table_not_exists(
    --Table id
    a_table_id raw,
    --Table name
    a_table_name varchar2)
as
    --Row count
    a_row_count number;
begin
    --Searching for the table id
    select count(*) into a_row_count
    from meta_tables
    where table_id = a_table_id;
    
    if a_row_count > 0 then
        raise_application_error(-20025,
                'A table with the specified id was already added (table_id = '
                || a_table_id
                || ') (#error_code = 20025)');
    end if;
    
    --Searching for the table name
    select count(*) into a_row_count
    from meta_tables
    where table_name = a_table_name;
    
    if a_row_count > 0 then
        raise_application_error(-20026,
                'A table with the specified name was already added (table_name = '
                || a_table_name
                || ') (#error_code = 20026)');
    end if;
    
end;

--go

--Function of checking table existence
create or replace procedure p_check_table_exists(
    --Table id
    a_table_id raw)
as
    --Row count
    a_row_count number;
begin
    --Searching for the table id
    select count(*) into a_row_count
    from meta_tables
    where table_id = a_table_id;
    
    if a_row_count = 0 then
        raise_application_error(-20027,
                'Specified table was not found (table_id = '
                || a_table_id
                || ') (#error_code = 20027)');
    end if;
end;

--go

--Function of creating new user table
create or replace procedure meta_add_table(
    --Table id
    a_table_id raw,
    --Table name
    a_table_name varchar2,
    --Id column id
    a_id_column_id raw,
    --Owner column id
    a_owner_column_id raw,
    --CreatedBy column id
    a_created_by_column_id raw,
    --CreatedOn column id
    a_created_on_column_id raw,
    --UpdatedBy column id
    a_updated_by_column_id raw,
    --UpdatedOn column id
    a_updated_on_column_id raw,
    --DeletedBy column id
    a_deleted_by_column_id raw,
    --DeletedOn column id
    a_deleted_on_column_id raw,
    --IsActive column_id
    a_is_active_column_id raw,
    --RowVersion column id
    a_row_version_column_id raw)
as
begin
    p_check_table_not_exists(a_table_id, a_table_name);

    --Inserting metadata
    insert into meta_tables(table_id, table_name)
    values (a_table_id, a_table_name);

    --Creating user table
    execute immediate 'create table tbl_'
    || a_table_name
    || '(id raw(16) not null, '
    || 'owner varchar2(26) not null, '
    || 'is_active number(1) default 1 not null, '
    || 'created_by varchar2(26) not null, '
    || 'created_on date not null, '
    || 'updated_by varchar2(26) not null, '
    || 'updated_on date not null, '
    || 'row_version raw(16) not null,'
    || 'deleted_by varchar2(26) null, '
    || 'deleted_on date null, '
    || 'check(length(trim(owner))>0),'
    || 'check(is_active=0 or is_active=1))';
    
    --Inserting system columns
    insert into meta_guid_columns(column_id, table_id,
    column_name, is_not_null, display_name, order_index, customization_level, description)
    values(a_id_column_id, a_table_id, 'id', 1, N'Id', 0, 0, N'The row id.');
    
    insert into meta_string_columns(column_id, table_id,
    column_name, is_not_null, display_name, order_index, customization_level, max_length, description)
    values(a_owner_column_id, a_table_id, 'owner', 1, N'Owner', 1, 0, 26, N'The row owner.');
    
    insert into meta_string_columns(column_id, table_id,
    column_name, is_not_null, display_name, order_index, customization_level, max_length, description)
    values(a_created_by_column_id, a_table_id, 'created_by', 1, N'Created by', 2, 0, 26, N'The name of the user that created the row.');
    
    insert into meta_datetime_columns(column_id, table_id,
    column_name, is_not_null, display_name, order_index, customization_level, description)
    values(a_created_on_column_id, a_table_id, 'created_on', 1, N'Created on', 3, 0, N'The date and time of the row creation.');
    
    insert into meta_string_columns(column_id, table_id,
    column_name, is_not_null, display_name, order_index, customization_level, max_length, description)
    values(a_updated_by_column_id, a_table_id, 'updated_by', 1, N'Updated by', 4, 0, 26, N'The name of the user that updated the row last.');
    
    insert into meta_datetime_columns(column_id, table_id,
    column_name, is_not_null, display_name, order_index, customization_level, description)
    values(a_updated_on_column_id, a_table_id, 'updated_on', 1, N'Updated on', 5, 0, N'The date and time of the last row update.');
    
    insert into meta_string_columns(column_id, table_id,
    column_name, is_not_null, display_name, order_index, customization_level, max_length, description)
    values(a_deleted_by_column_id, a_table_id, 'deleted_by', 0, N'Deleted by', 6, 0, 26, N'The name of the user that deleted the row.');
    
    insert into meta_datetime_columns(column_id, table_id,
    column_name, is_not_null, display_name, order_index, customization_level, description)
    values(a_deleted_on_column_id, a_table_id, 'deleted_on', 0, N'Deleted on', 7, 0, N'The date and time of the row deletion.');
    
    insert into meta_bool_columns(column_id, table_id,
    column_name, is_not_null, display_name, order_index, customization_level, description)
    values(a_is_active_column_id, a_table_id, 'is_active', 1, N'Is active', 8, 0, N'True if the row was not soft deleted.');
    
     insert into meta_guid_columns(column_id, table_id,
    column_name, is_not_null, display_name, order_index, customization_level, description)
    values(a_row_version_column_id, a_table_id, 'row_version', 1, N'Row version', 9, 0, N'The version of the row.');
        
    --Creating table view
    p_create_table_view(a_table_id);

    --Creating table triggers
    p_create_table_triggers(a_table_id);
    
    --Granting access to schema administrators
    for u in (select user_name from meta_users
        where can_change_schema = 1) loop
        execute immediate 'grant select, insert, update, delete on tbl_' || a_table_name || ' to ' || u.user_name;
    end loop;    
end;

--go

--Function of deleting user table
create or replace procedure meta_delete_table(
    --Table id
    a_table_id raw)
as
    --Table name
    a_table_name varchar(26);
begin
    p_check_table_exists(a_table_id);

    a_table_name := p_get_table_name(a_table_id);

    --Deleting the metadata
    delete from meta_tables
    where table_id = a_table_id;

    --Dropping table view
   p_drop_view_if_exists(a_table_name);
    
    --Dropping the table
    p_drop_table_if_exists('tbl_' || a_table_name);
end;

--go

--Function of table renaming
create or replace procedure meta_rename_table(
    --Table id
    a_table_id raw,
    --New table name
    a_new_table_name varchar2)
as
    --Old table name
    a_old_table_name varchar2(26);
    --Row count
    a_row_count number;
begin
    p_check_table_exists(a_table_id);
    
    --Searching for a table with the same name
    select count(*) into a_row_count
    from meta_tables
    where table_name = a_new_table_name;
    
    if a_row_count > 0 then
        raise_application_error(-20026,
            'A table with the specified name was already added (table_name = '
            || a_new_table_name
            || ') (#error_code = 20026)');
    end if;
    
    a_old_table_name := p_get_table_name(a_table_id);

    --If there is no need for renaming
    if a_old_table_name = a_new_table_name then
        return;
    end if;
    
    --Dropping table view
    p_drop_view_if_exists(a_old_table_name);
    
    --Renaming the table
    execute immediate 'alter table tbl_' || a_old_table_name
    || ' rename to tbl_' || a_new_table_name;
    
    --Updating the metadata
    update meta_tables
    set table_name = a_new_table_name
    where table_id = a_table_id;
    
     --Renaming all index triggers for table columns
    for item in (select column_id from meta_table_columns where table_id=a_table_id) loop
        --Recreating column index triggers
        p_recreate_col_index_triggers(item.column_id);
    end loop;
    
    --Creating table view
    p_create_table_view(a_table_id);
end;

--go

--Function of setting the availability of the recycle bin
create or replace procedure meta_set_recycle_bin(
    --The availability of the recycle bin
    a_recycle_bin_enabled numeric)
as
begin
    --Updating the metadata
    update meta_options
    set int_value = a_recycle_bin_enabled
    where option_name = 'RECYCLE_BIN';
    
    --Recreating table views
    for t in (select table_id, table_name from meta_tables) loop
        p_drop_view_if_exists(t.table_name);
        p_create_table_view(t.table_id);
    end loop;
end;

--go
